/*
 * Project: Sudoku Explainer
 * Copyright (C) 2006-2007 Nicolas Juillerat
 * 
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.uniqueness;

import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.R;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;
import com.onegravity.sudoku.solver.data.Region;
import com.onegravity.sudoku.solver.util.HtmlLoader;
import com.onegravity.sudoku.solver.util.ValuesFormatter;

public class UniquenessTestType3HiddenHint extends UniquenessTestHint {

	private static final long serialVersionUID = 4647273495766975098L;
	
	private final Cell c1;
    private final Cell c2;
    private final int[] otherValues;
    private final Region region;
    private final BitSet hiddenValues;
    private final int[] hiddenIndexes; // indexes of the hidden set

    public UniquenessTestType3HiddenHint(List<Cell> loop, int v1, int v2,
            Map<Cell, BitSet> removablePotentials, Cell c1, Cell c2, int[] otherValues, BitSet hiddenValues,
            Region region, int[] indexes) {
        super(loop, v1, v2, removablePotentials);
        this.c1 = c1;
        this.c2 = c2;
        this.otherValues = otherValues;
        this.hiddenValues = hiddenValues;
        this.region = region;
        this.hiddenIndexes = indexes;
    }

    @Override
    public Collection<Cell> getSelectedCells(HintType hintType) {
    	switch(hintType) {
    	case SMALL_CLUE: return super.getSelectedCells(hintType);
    	case HINT: 
    		// doesn't look good
    		// return Arrays.asList(new Cell[] {c1, c2}); 
    	case BIG_CLUE:
    	default: return null;
    	}
    }

    @Override
    public Map<Cell, BitSet> getSelectedPotentials(HintType hintType, int viewNum) {
        return (hintType!=HintType.SMALL_CLUE) ? super.getSelectedPotentials(hintType, viewNum) : null;
    }

    @Override
    public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType hintType) {
        return (hintType==HintType.HINT) ? super.getRemovedPotentials(hintType) : null;
    }

    @Override
    public Region[] getRegions(HintType hintType) {
        return (hintType==HintType.HINT) ? new Region[] {region} : null;
    }

    @Override
    public Map<Cell, BitSet> getSecondarySelectedPotentials(HintType hintType, int viewNum) {
        Map<Cell, BitSet> result = new HashMap<Cell, BitSet>();
        appendOrangePotentials(result);
        return (hintType!=HintType.SMALL_CLUE) ? result : null;
    }

    private Map<Cell, BitSet> appendOrangePotentials(Map<Cell, BitSet> potentials) {
        for (int i = 0; i < hiddenIndexes.length; i++) {
            int index = hiddenIndexes[i];
            Cell cell = region.getCell(index);
            processCell(potentials, cell);
        }
        // Add the two cells of the loop
        processCell(potentials, c1);
        processCell(potentials, c2);
        return potentials;
    }

    private void processCell(Map<Cell, BitSet> potentials, Cell cell) {
        BitSet values = potentials.get(cell);
        if (values == null) {
            values = (BitSet) cell.getPotentialValues().clone();
            potentials.put(cell, values);
        }
        values.and(hiddenValues);
    }
    
    @Override
    public double getDifficulty() {
        double toAdd = (hiddenIndexes.length - 1) * 0.1; // Pair=0.1, Quad=0.3
        return super.getDifficulty() + toAdd;
    }

    @Override
    protected int getType() {
        return 3;
    }
    
    @Override
    protected HintResource getResource() {
        return HintResource.UNIQUENESS_TEST_3_HIDDEN;
    }

    protected String getAppendix() {
    	return " (" + HintResource.getString(R.string.hint_hidden) + " " + HintResource.getString(setNamesRes[hiddenValues.cardinality() - 2]) + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    @Override
    public String getHintHtml(HintType hintType) {
        String andString = HintResource.getString(R.string.and);
        String orString = HintResource.getString(R.string.or);
        String result = HtmlLoader.loadHtml(HintResource.UNIQUENESS_TEST_3_HIDDEN.hint(hintType));
        String type = getTypeName();
        Cell[] loopCells = new Cell[loop.size()];
        loop.toArray(loopCells);
        String allCells = Cell.toString(loopCells);
        String cell1 = c1.toString();
        String cell2 = c2.toString();
        String valuesOrName = ValuesFormatter.formatValues(otherValues, " " + orString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String setName = HintResource.getString(setNamesRes[hiddenValues.cardinality() - 2]);
        Cell[] cells = new Cell[hiddenIndexes.length];
        for (int i = 0; i < cells.length; i++) {
            cells[i] = region.getCell(hiddenIndexes[i]);
        }
        String otherCells = ValuesFormatter.formatCells(cells, " " + andString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String valuesAndName = ValuesFormatter.formatValues(hiddenValues, " " + andString + " "); //$NON-NLS-1$ //$NON-NLS-2$
        String regionName = region.toString();
        result = HtmlLoader.format(result, type, v1, v2, allCells, cell1, cell2, valuesOrName, setName, otherCells, valuesAndName, regionName, loop.size());
        return result;
    }

    /**
     * Overriden to differentiate hints with different naked sets.
     * <p>
     * Because we only make different objects that are equal according
     * to <tt>super.equals()</tt>, <tt>hashCode()</tt> does not need
     * to be overriden.
     */
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof UniquenessTestType3HiddenHint))
            return false;
        if (!super.equals(o))
            return false;
        UniquenessTestType3HiddenHint other = (UniquenessTestType3HiddenHint)o;
        if (!this.region.equals(other.region))
            return false;
        if (!this.hiddenValues.equals(other.hiddenValues))
            return false;
        if (this.hiddenIndexes.length != other.hiddenIndexes.length)
            return false;
        for (int i = 0; i < hiddenIndexes.length; i++) {
            if (this.hiddenIndexes[i] != other.hiddenIndexes[i])
                return false;
        }
        return true;
    }

}
